home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1994 / MacHack 1994.toast / MacHack™94 / Talks & Papers / Michael D. Crawford↵ / Word Services SDK 1.0.5 / Writeswell Jr. Source / ServiceMgr.c < prev    next >
Text File  |  1993-03-17  |  11KB  |  485 lines

  1. /* ServiceMgr.c
  2.  * Handle the Services menu in Writeswell, Jr.
  3.  * ©1992 Working Software, Inc.
  4.  * This source code is copyrighted.  Permission is granted to use the Word Services
  5.  * portion of the Writeswell Jr. source code in your own programs, but you 
  6.  * may not distribute the Writeswell Jr. word-processor code as a 
  7.  * commercial product.  If you modify the code, please do not call it 
  8.  * Writeswell Jr. (or Writeswell.)  This will ensure that people understand the 
  9.  * program and don’t have to deal with a number of different versions with 
  10.  * who-knows-what going on in the code.
  11.  * 
  12.  * Writeswell Jr. and Writeswell are trademarks of Working Software, Inc.
  13.  * 19 Apr 92 Mike Crawford
  14.  */
  15.  
  16. #include <Aliases.h>
  17. #include <EPPC.h>
  18. #include <AppleEvents.h>
  19. #include <AEObjects.h>
  20. #include <AEPackObject.h>
  21. #include "AERegistry.h"
  22. #include "WordServices.h"
  23. #include "TestBed.h"
  24. #include "TBConstants.h"
  25. #include "AppEvents.h"
  26. #include "AEObj.h"
  27. #include "Gripe.h"
  28. #include "Prefs.h"
  29. #include "DoChecking.h"
  30. #include "ObText.h"
  31. #include "InitMenu.h"
  32. #include "ServiceMgr.h"
  33. #include "TBGlobals.h"
  34.  
  35. OSErr GetNewBatchService( void )
  36. {
  37.     AEAddressDesc    spellerAddr;
  38.     AEDesc            stringDesc;
  39.     AEDesc            aliasDesc;
  40.     AEDesc            iconDesc;
  41.     OSErr            err;
  42.     
  43.     if ( CountServices() >= kMaxServices ){
  44.         RealGripe( "\pNo more services may be added" );
  45.         return noErr;
  46.     }
  47.  
  48.     /* Look for a currently running speller */
  49.  
  50.     err = GetSpellerAddress( &spellerAddr );
  51.     if ( err ){
  52.         return ( err == userCanceledErr ? noErr : err );
  53.     }
  54.  
  55.     /*****
  56.      * Request the speller's batch menu string
  57.      *****/
  58.     
  59.     err = GetAppProperty( &spellerAddr, pBatchMenuString, typePString, &stringDesc );
  60.     if ( err ){
  61.         Gripe( "\pGetAppProperty failed to get menu string" );
  62.         return err;
  63.     }
  64.     
  65.     /*DebugStr( *(stringDesc.dataHandle) );*/    /* Uncomment this to look at string */
  66.     
  67.     
  68.     /*****
  69.      * Request the speller's location alias
  70.      *****/
  71.  
  72.     err = GetAppProperty( &spellerAddr, pLocation, typeAlias, &aliasDesc );
  73.     if ( err ){
  74.         Gripe( "\pGetAppProperty failed to get location alias" );
  75.         return err;
  76.     }
  77.     
  78.     /*
  79.         pMenuIcon
  80.         Description:    The value of this property is a small icon that may be
  81.                         placed in the menu  along with the interactive or batch
  82.                         menu strings.  It should be identical to the small icon
  83.                         that the speller shows in the Finder.  Word processors that
  84.                         take advantage of this can use it to make the different menu
  85.                         items more distinguishable (a user might have two different
  86.                         spelling checkers).
  87.         Object Class ID:    typePixelMap
  88.      */
  89.  
  90.     iconDesc.dataHandle = (Handle)NULL;
  91.     iconDesc.descriptorType = typeNull;
  92.  
  93.     err = GetAppProperty( &spellerAddr, pMenuIcon, typeSmallIcon, &iconDesc );
  94.     
  95.     /* It is permissible for the icon not to be present.  We just won't display one.
  96.      */
  97.  
  98.     if ( err && err != errAENoSuchObject ){
  99.         Gripe( "\pGetAppProperty failed to get speller menu icon" );
  100.         return err;
  101.     }
  102.     
  103.     /* Save the information in the preferences file */
  104.     
  105.     err = SaveServiceInfo( kBatchService, stringDesc, aliasDesc, iconDesc );
  106.     if ( err ){
  107.         Gripe( "\pCould not save service info" );
  108.         return err;
  109.     }
  110.     
  111.     RebuildServiceMenu();
  112.     
  113.     err = AEDisposeDesc( &stringDesc );
  114.     if ( err ){
  115.         Gripe( "\pAEDisposeDesc failed" );
  116.         return err;
  117.     }
  118.  
  119.     err = AEDisposeDesc( &aliasDesc );
  120.     if ( err ){
  121.         Gripe( "\pAEDisposeDesc failed" );
  122.         return err;
  123.     }
  124.  
  125.     err = AEDisposeDesc( &iconDesc );
  126.     if ( err ){
  127.         Gripe( "\pAEDisposeDesc failed" );
  128.         return err;
  129.     }
  130.  
  131.     err = AEDisposeDesc( &spellerAddr );
  132.     if ( err ){
  133.         Gripe( "\pAEDisposeDesc failed" );
  134.         return err;
  135.     }
  136.     spellerAddr.descriptorType = typeNull;
  137.     spellerAddr.dataHandle = (Handle)NULL;
  138.  
  139.     return noErr;
  140. }
  141.  
  142. OSErr GetAppProperty( AEAddressDesc *spellerAddrPtr,
  143.                         DescType propCode,
  144.                         DescType desiredType,
  145.                         AEDesc *resultPtr )
  146. {
  147.     AEDesc            errDesc;
  148.     AppleEvent        getDataEvent;
  149.     AppleEvent        replyEvent;
  150.     AEDesc            nullDesc;
  151.     AEDesc            propSpec;
  152.     AEDesc            propDesc;
  153.     OSErr            err;
  154.  
  155.     /* Create the descriptor for the container (the application, or null) */
  156.  
  157.     err = AECreateDesc( typeNull, (Ptr)NULL, (Size)0, &nullDesc );
  158.     if ( err )
  159.         return err;
  160.     
  161.     /* Create the key data, which gives the code for the desired property */
  162.  
  163.     err = AECreateDesc( typeType, (Ptr)&propCode, sizeof( propCode ), &propDesc );
  164.     if ( err )
  165.         return err;
  166.  
  167.     /* Create the Object Specifier for the menu string property */
  168.     
  169.     err = CreateObjSpecifier( typeProperty,
  170.                                 &nullDesc,
  171.                                 formPropertyID,
  172.                                 &propDesc,
  173.                                 true,                    /* Dispose of input descriptors */
  174.                                 &propSpec );
  175.     if ( err )
  176.         return err;
  177.  
  178.     /* Create the event to send to the speller */
  179.     
  180.     err = AECreateAppleEvent( kAECoreSuite,
  181.                                 kAEGetData,
  182.                                 spellerAddrPtr,
  183.                                 kAutoGenerateReturnID,
  184.                                 kAnyTransactionID,
  185.                                 &getDataEvent );
  186.     
  187.     if ( err ){
  188.         Gripe( "\pcreate getd event failed" );
  189.         return err;
  190.     }
  191.     
  192.     /* Insert the object specifier as the direct object of the batch event */
  193.  
  194.     err = AEPutParamDesc( &getDataEvent,
  195.                             keyDirectObject,
  196.                             &propSpec );
  197.     if ( err ){
  198.         Gripe( "\pAEPutParamDesc failed to put direct object on Get Data event" );
  199.         return err;
  200.     }
  201.     err = AEDisposeDesc( &propSpec );
  202.     if ( err ){
  203.         Gripe( "\pAEDisposeDesc failed" );
  204.         return err;
  205.     }
  206.  
  207.     /* Send the event.  We await the reply, so that if there is a failure of some
  208.      * sort in the initial connection, we can alert the user right away.  The timeout
  209.      * value to use here should be as long as one would care to have a user wait for
  210.      * the completion of a menu command.  Since we expect that the speller is on a local
  211.      * machine in this case, and should be able to respond immediately, we just give
  212.      * a few seconds for the timeout.
  213.      *
  214.      * We should assign an idle proc to spin the cursor.  Even better would be a progress
  215.      * dialog that says "Contacting speller" or some such, with an animated display that
  216.      * shows the time elapsed relative to the total timeout, so the user will know how
  217.      * long she may have to wait
  218.      */
  219.  
  220. #define kFewSeconds 300
  221.     
  222.     err = AESend( &getDataEvent,
  223.                     &replyEvent,
  224.                     kAEWaitReply + kAENeverInteract,
  225.                     kAENormalPriority,
  226.                     kFewSeconds,
  227.                     (IdleProcPtr)NULL,
  228.                     (EventFilterProcPtr)NULL );
  229.     
  230.     if ( err ){
  231.         Gripe( "\psend getd event failed" );
  232.         return err;
  233.     }
  234.     err = AEDisposeDesc( &getDataEvent );
  235.     if ( err ){
  236.         Gripe( "\pAEDisposeDesc failed" );
  237.         return err;
  238.     }
  239.  
  240.     /* At this point we have received the client's reply event.  Check for an error
  241.      * result.
  242.      */
  243.     
  244.     err = AEGetParamDesc( &replyEvent,
  245.                             keyErrorNumber,
  246.                             typeShortInteger,
  247.                             &errDesc );
  248.  
  249.     if ( err == errAEDescNotFound ){
  250.         /* There is no error value - get the data from the reply event.
  251.          * This will call our coercion routine to convert the text to a Pascal string -
  252.          * the data is actually either typeIntlText or typeChar.
  253.          */
  254.  
  255.         err = AEGetParamDesc( &replyEvent,
  256.                                 keyDirectObject,
  257.                                 desiredType,
  258.                                 resultPtr );
  259.         if ( err ){
  260.             Gripe( "\pCannot get reply value" );
  261.             return err;
  262.         }        
  263.     
  264.         /* MDC 1.1.1 */
  265.  
  266.         err = AEDisposeDesc( &replyEvent );
  267.         if ( err ){
  268.             Gripe( "\pAEDisposeDesc failed" );
  269.             return;
  270.         }
  271.  
  272.     } else {
  273.         err = AEDisposeDesc( &replyEvent );
  274.         if ( err ){
  275.             Gripe( "\pAEDisposeDesc failed" );
  276.             return;
  277.         }
  278.     
  279.         err = **(short**)(errDesc.dataHandle);
  280.         /* err = */ AEDisposeDesc( &propSpec );
  281.     
  282.         if ( err ){
  283.             if ( err == errAENoSuchObject )
  284.                 return err;                        /* This is an OK error */
  285.             else{
  286.                 Gripe( "\pError result returned from speller" );
  287.                 return err;
  288.             }
  289.         }
  290.     }
  291.  
  292.     return noErr;
  293. }
  294. void RebuildServiceMenu( void )
  295. {
  296.     MenuHandle        servMenu;
  297.     short            numItems;
  298.     short            i;
  299.     
  300.     servMenu = GetServiceMenu();
  301.     if ( !servMenu ){
  302.         Gripe( "\pCannot get service menu handle" );
  303.         return;
  304.     }
  305.     
  306.     numItems = CountMItems( servMenu );
  307.     
  308.     for ( i = numItems; i > kSMDash; i-- ){
  309.         DelMenuItem( servMenu, i );
  310.     }
  311.     
  312.     BuildServiceMenu();
  313.     
  314.     return;
  315. }
  316.  
  317. OSErr GetSpellerAddress( AEAddressDesc    *spellerAddrPtr )
  318. {
  319.     PortInfoRec portInfo;
  320.     TargetID    targetID;
  321.     OSErr        err;
  322.     AEAddressDesc    spellerAddr;
  323.  
  324.     err = GetTargetAddress( (StringPtr)"\pChoose a Word Services Server",
  325.                             (StringPtr)"\pApple Event Aware Programs",
  326.                             &portInfo,
  327.                             spellerAddrPtr,
  328.                             (StringPtr)"\pWORDSERVICES",
  329.                             &targetID );
  330.     return err;
  331. }
  332.  
  333. short CountServices( void )
  334. {
  335.     WWJrPrefsHdl    prefHdl;
  336.     short            i;
  337.     short            count;
  338.     
  339.     prefHdl = GetPrefHandle();
  340.     if ( !prefHdl ){
  341.         Gripe( "\pCannot get preferences handle" );
  342.         return kMaxServices;
  343.     }
  344.  
  345.     count = 0;
  346.     
  347.     for ( i = 0; i < kMaxServices; i++ ){
  348.         if ( (*prefHdl)->serviceType[ i ] != kNoService )
  349.             count++;
  350.     }
  351.     
  352.     return count;
  353. }
  354.  
  355. short GetServiceSlot( void )
  356. {
  357.     WWJrPrefsHdl    prefHdl;
  358.     short            i;
  359.     
  360.     prefHdl = GetPrefHandle();
  361.     if ( !prefHdl ){
  362.         Gripe( "\pCannot get preferences handle" );
  363.         return kMaxServices;
  364.     }
  365.     
  366.     for ( i = 0; i < kMaxServices; i++ ){
  367.         if ( (*prefHdl)->serviceType[ i ] == kNoService )
  368.             return i;
  369.     }
  370.     
  371.     return kMaxServices;            /* No Free slots */
  372. }
  373.  
  374. OSErr SaveServiceInfo( ServiceType serviceType,
  375.                         AEDesc menuDesc,
  376.                         AEDesc aliasDesc,
  377.                         AEDesc iconDesc )
  378. {
  379.     short            slot;
  380.     WWJrPrefsHdl    prefHdl;
  381.     short            resID;
  382.     short            iconResID;
  383.     Handle            resHandle;
  384.     short            curFile;
  385.     OSErr            err;
  386.  
  387.     slot = GetServiceSlot();
  388.     
  389.     if ( slot == kMaxServices ){
  390.         Gripe( "\pOut of slots for new services" );
  391.         return ioErr;                                /* Not sure what a good error would be */
  392.     }
  393.     
  394.     prefHdl = GetPrefHandle();
  395.     if ( !prefHdl ){
  396.         Gripe( "\pCannot get preferences handle" );
  397.         return resNotFound;
  398.     }
  399.  
  400.     resID = kServiceBaseID + slot;
  401.     
  402.     curFile = CurResFile();
  403.     UseResFile( gPrefFileRefNum );
  404.     
  405.     /* Make sure there's no old resources around... there shouldn't be */
  406.  
  407.     resHandle = GetResource( 'STR ', resID );
  408.     if ( resHandle )
  409.         RmveResource( resHandle );
  410.  
  411.     resHandle = GetResource( rAliasType, resID );
  412.     if ( resHandle )
  413.         RmveResource( resHandle );
  414.     
  415.     /* Copy the string into the resource file */
  416.     
  417.     resHandle = menuDesc.dataHandle;
  418.     
  419.     err = HandToHand( &resHandle );
  420.     if ( err ){
  421.         UseResFile( curFile );
  422.         return err;
  423.     }
  424.     
  425.     AddResource( resHandle, 'STR ', resID, "\p" );
  426.     err = ResError();
  427.     if ( err ){
  428.         DisposHandle( resHandle );
  429.         UseResFile( curFile );
  430.         return err;
  431.     }
  432.     WriteResource( resHandle );
  433.     
  434.     /* Copy the alias record into the resource file */
  435.     
  436.     resHandle = aliasDesc.dataHandle;
  437.     
  438.     err = HandToHand( &resHandle );
  439.     if ( err ){
  440.         UseResFile( curFile );
  441.         return err;
  442.     }
  443.     
  444.     AddResource( resHandle, rAliasType, resID, "\p" );    /* rAliasType is 'alis' */
  445.     err = ResError();
  446.     if ( err ){
  447.         DisposHandle( resHandle );
  448.         UseResFile( curFile );
  449.         return err;
  450.     }
  451.     WriteResource( resHandle );
  452.  
  453.     /* Copy the menu icon into the resource file */
  454.     
  455.     resHandle = iconDesc.dataHandle;
  456.  
  457.     if ( iconDesc.descriptorType != typeNull && resHandle != (Handle)NULL ){
  458.     
  459.         err = HandToHand( &resHandle );
  460.         if ( err ){
  461.             UseResFile( curFile );
  462.             return err;
  463.         }
  464.         
  465.         iconResID = kMenuIconBaseID + slot;
  466.         
  467.         AddResource( resHandle, 'SICN', iconResID, "\p" );
  468.         err = ResError();
  469.         if ( err ){
  470.             DisposHandle( resHandle );
  471.             UseResFile( curFile );
  472.             return err;
  473.         }
  474.         WriteResource( resHandle );
  475.     }
  476.  
  477.     (*prefHdl)->serviceType[ slot ] = serviceType;
  478.     
  479.     ChangedResource( prefHdl );
  480.     WriteResource( prefHdl );
  481.     
  482.     UseResFile( curFile );
  483.  
  484.     return noErr;
  485. }